//
// JsonRpc++ Copyright (c) 2007 by Peter Wang
//
#ifndef _JSONRPCCLIENT_H_
#define _JSONRPCCLIENT_H_
#if defined(_MSC_VER)
# pragma warning(disable:4786)    // identifier was truncated in debug info
#endif


#ifndef MAKEDEPEND
# include <string>
#endif

#include "JsonRpcUtil.h"
#include "JsonRpcDispatch.h"
#include "JsonRpcSource.h"

namespace JsonRpc {


  //! A class to send JSON RPC requests to a server and return the results.
  class JsonRpcClient : public JsonRpcSource {
  private:
	  static int codePage;
  public:
    //! Construct a client to connect to the server at the specified host:port address
    //!  @param host The name of the remote machine hosting the server
    //!  @param port The port on the remote machine where the server is listening
    //!  @param uri  An optional string to be sent as the URI in the HTTP GET header
    JsonRpcClient(const char* host, int port, const char* uri=0);
    JsonRpcClient(const char* host, int port, const char* uri, const char *user, const char *password);

    //! Destructor
    virtual ~JsonRpcClient();

    //! Execute the named procedure on the remote server.
    //!  @param method The name of the remote procedure to execute
    //!  @param params An array of the arguments for the method
    //!  @param result The result value to be returned to the client
    //!  @return true if the request was sent and a result received 
    //!   (although the result might be a fault).
    //!
    //! Currently this is a synchronous (blocking) implementation (execute
    //! does not return until it receives a response or an error). Use isFault()
    //! to determine whether the result is a fault response.
    bool execute(const char* method, Json::Value const& params, Json::Value& result, bool throwException=true);

	static void set_code_page(int cp) {
		codePage = cp;
	}

#if (defined(_MSC_VER) && (_MSC_VER > 1200))
    template < class R >  
    R execute(const char *method)
    {
    Json::Value params, result;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 >  
    R execute(const char *method, T1 a1)
    {
    Json::Value params, result;

    params[0U] = a1;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2>  
    R execute(const char *method, T1 a1, T2 a2)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2, class T3>  
    R execute(const char *method, T1 a1, T2 a2, T3 a3)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;
    params[2] = a3;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2, class T3, class T4>  
    R execute(const char *method, T1 a1, T2 a2, T3 a3, T4 a4)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;
    params[2] = a3;
    params[3] = a4;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2, class T3, class T4, class T5>  
    R execute(const char *method, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;
    params[2] = a3;
    params[3] = a4;
    params[4] = a5;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2, class T3, class T4, class T5, class T6>  
    R execute(const char *method, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;
    params[2] = a3;
    params[3] = a4;
    params[4] = a5;
    params[5] = a6;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }

    template < class R, class T1 , class T2, class T3, class T4, class T5, class T6, class T7>  
    R execute(const char *method, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)
    {
    Json::Value params, result;

    params[0U]= a1;
    params[1] = a2;
    params[2] = a3;
    params[3] = a4;
    params[4] = a5;
    params[5] = a6;
    params[6] = a7;

    execute(method, params, result);

    return impl::getValue < R > (result);
    }
#endif


    //! Returns true if the result of the last execute() was a fault response.
    bool isFault() const { return _isFault; }


    // JsonRpcSource interface implementation
    //! Close the connection
    virtual void close();

    //! Handle server responses. Called by the event dispatcher during execute.
    //!  @param eventType The type of event that occurred. 
    //!  @see JsonRpcDispatch::EventType
    virtual unsigned handleEvent(unsigned eventType);

  protected:
    // Execution processing helpers
    virtual bool doConnect();
    virtual bool setupConnection();

    virtual bool generateRequest(const char* method, Json::Value const& params);
    virtual std::string generateHeader(std::string const& body);
    virtual bool writeRequest();
    virtual bool readHeader();
    virtual bool readResponse();
    virtual bool parseResponse(Json::Value& result);

	std::string _id;
    // Possible IO states for the connection
    enum ClientConnectionState { NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER, READ_RESPONSE, IDLE };
    ClientConnectionState _connectionState;

    // Server location
    std::string _host;
    std::string _uri;
    int _port;
	std::string _user;
	std::string _password;

    // The json-encoded request, http header of response, and response message 
    std::string _request;
    std::string _header;
    std::string _response;

    // Number of times the client has attempted to send the request
    int _sendAttempts;

    // Number of bytes of the request that have been written to the socket so far
    int _bytesWritten;

    // True if we are currently executing a request. If you want to multithread,
    // each thread should have its own client.
    bool _executing;

    // True if the server closed the connection
    bool _eof;

    // True if a fault response was returned by the server
    bool _isFault;

    // Number of bytes expected in the response body (parsed from response header)
    int _contentLength;

    // Event dispatcher
    JsonRpcDispatch _disp;

  };	// class JsonRpcClient

}	// namespace JsonRpc

#endif	// _JSONRPCCLIENT_H_
